package cn.tedu.reflection;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

/*本类用于反射测试*/
public class TestReflect {
    /*单元测试方法:是java中最小的测试单位,使用灵活,推荐指数:5颗星
    * 语法要求:@Test + public + void + 没有参数
    * 执行方式:选中当前要测试方法名前的绿色小三角,成功执行会有绿色小对勾*/
    //注意,JUnit单元测试框架使用时成员调入资源,选择"Add JUnit4 to classpath"
    //成功导入会显示导包语句:import org.junit.Test
    @Test
    public void getclazz() throws ClassNotFoundException {
        Class<?> clazz1 = Class.forName("cn.tedu.reflection.TestReflect");
        Class<?> clazz2= Student.class;
        Class<?> clazz3 = new Student().getClass();
        System.out.println(clazz1);
        System.out.println(clazz1.getName());
        //目标类类名
        System.out.println(clazz1.getSimpleName());
        System.out.println(clazz1.getPackage());
        //先获取包对象,再通过包对象获取包对象的名字
        System.out.println(clazz1.getPackage().getName());
    }
    /*通过单元测试方法,获取Student目标类中的成员变量Field*/

    @Test
    public void getFie() throws ClassNotFoundException {

        Class<?> clazz = Class.forName("cn.tedu.reflection.Student");
        Class<?> clazz3 = Student.class;
        Class<?> clazz2 = new Student().getClass();
        Field[] fs = clazz.getFields();
        /*注意!目前要获取的成员变量的修饰符必须是public才能获取到
        * 不然,像默认修饰符的成员变量也是获取不到的*/
        for (Field f:fs){
            System.out.println("属性名"+f.getName());
            System.out.println("属性类型"+f.getType());
        }
    }
    /*辅助理解的单元测试方法*/
    @Test
    public void stu(){
        Student s1 = new Student("张三",5234);
        Student s2 = new Student("李四",512);
        Student s3 = new Student("王五",15);

        Student[] S= {s1,s2,s3};
        for(Student STU:S){
            System.out.println("名字是:"+STU.name);
            System.out.println("年龄是:"+STU.age);
            STU.play();
            STU.eat(2);
        }
    }

    /*通过单元测试方法,获取Student目标类中的成员方法*/
    @Test
    public void getFunction(){
        //获取字节码对象
        Class<?> clazz = Student.class;
        //通过字节码对象获取目标类对应的所有成员方法们
        Method[] ms = clazz.getMethods();
        //通过高效for循环遍历数组,每轮循环拿到一个方法对象
        for(Method m : ms){
            System.out.println("遍历啦:");
            System.out.println("方法名字:"+m.getName());
            //通过本轮循环获取到方法对象,进一步获取这个方法的参数类型
            Class<?>[] pt = m.getParameterTypes();
            System.out.println(Arrays.toString(pt));
        }
    }

    /*通过单元测试方法,获取Student目标类中的构造方法*/
    @Test
    public void getCons(){
        //获取字节码对象
        Class<?> clazz = new Student().getClass();
        //通过字节码对象   获取目标类对应的所有构造方法们
        Constructor<?>[] cs = clazz.getConstructors();
        //通过高效for循环遍历,每轮循环拿到一个构造函数对象
        for(Constructor c:cs){
            System.out.println("又要遍历啦");
            System.out.println("构造方法名:"+c.getName());
            Class[] pt = c.getParameterTypes();
            System.out.println("构造方法参数类型"+Arrays.toString(pt));
        }
    }

    /*通过单元测试方法,利用反射创建目标类对象*/
    @Test
    public void getObject() throws Exception {
        //获取字节码对象
        Class<?> clazz = Student.class;
        //利用反射创建目标业务类对象
        /*反射创建对象的方案1:通过触发目标类的无参构造创建对象*/
        Object o = clazz.newInstance();
        System.out.println(o);//Student{name='null', age=0}

        /*反射创建对象的方法2:先获取指定的全参构造对象,再通过构造对象创建Student对象*/
        //获取指定的构造函数对象,主意要获取构造方法,需要指定参数类型,而且是字节码对象
        Constructor<?> c = clazz.getConstructor(String.class, int.class);
        //利用获取到的全参构造对象,创建目标类Student的对象
        Object o2 = c.newInstance("赵六", 6);
        //打印创建好的学生类对象,可以看到属性也赋值了
        System.out.println(o2);

    }

}


